"use strict";

// 全局变量，用于存放一些初始化完成的插件变量，使用key:value方式存储
// 缓存
window.EUtilCache = {};
// 缓存序号
window.EUtilCacheSN = 1;

/**
 * 提供读写缓存数据、获取URL参数、判断设备类型、窗口尺寸、UUID、isXXX、提示、设置等待状态、ajax等公用方法
 */
let EUtil = function () {
    // 窗口大小改变时的回调
    let resizeHandlers = [];

    /**
     * 响应式配置
     */
    let breakpoints = {
        sm: 544, // 小屏幕 - 手机
        md: 768, // 中等屏幕 - pad
        lg: 1024, // 大屏 - 桌面
        xl: 1200, // xl 大屏 - 桌面
        xxl: 1400, // xxl 大屏 - 桌面
        xxxl: 1600 // xxxl 大屏 - 桌面
    };

    /**
     * 处理窗口大小调整事件方法中加入事件防抖，拖动后`250ms`后执行；
     * 你可以通过`EUtil.addResizeHandler(callback)`和`EUtil.removeResizeHandler(callback)`添加或删除回调
     */
    let windowResizeHandler = function () {
        let timeout = null; // 定时器
        let delay = 250; // 延迟时间

        $(window).on('resize', function () {
            clearTimeout(timeout);
            timeout = setTimeout(function () {
                EUtil.runResizeHandlers();
            }, delay);
        });
    };


    return {
        /**
         * 调用`windowResizeHandler`，监听了`window`的`resize`事件
         */
        init: function () {
            windowResizeHandler();
        },
        /**
         * 添加窗口大小调整回调，无需考虑防抖，已经在`windowResizeHandler`统一处理
         *
         * @param callback {function} 回调函数
         */
        addResizeHandler: function (callback) {
            resizeHandlers.push(callback);
        },

        /**
         * 移除窗口大小调整回调
         *
         * @param callback {function} 回调函数
         */
        removeResizeHandler: function (callback) {
            for (let i = 0; i < resizeHandlers.length; i++) {
                if (callback === resizeHandlers[i]) {
                    delete resizeHandlers[i];
                }
            }
        },
        /**
         * 触发窗口大小调整事件，会调用`resizeHandlers`中所有回调
         */
        runResizeHandlers: function () {
            for (let i = 0; i < resizeHandlers.length; i++) {
                if (EUtil.isFunction(resizeHandlers[i])) {
                    resizeHandlers[i].call();
                }
            }
        },
        /**
         * 全局变量，一般用于存放一些与元素绑定的变量
         *
         * @param element {object} html对象或jquery对象
         */
        data: function (element) {
            let $element = $(element);
            // 序号在元素上的属性名
            let snKey = 'e-cache-sn';
            // 序号
            let serialNumber = $element.data(snKey);
            return {
                /**
                 * 根据`key`获取元素变量的`value`，如果没有指定的`key`将返回`null`
                 *
                 * @param key {string} key
                 * @returns {null|*}
                 */
                get: function (key) {
                    // 如果为多个元素或者data-e-cache为空，无法获取对象，直接返回null
                    if ($element.length !== 1 || EUtil.isBlank(serialNumber)) {
                        return null;
                    }
                    return EUtilCache[serialNumber][key];
                },
                /**
                 * 向全局变量中添加值
                 *
                 * @param key {string} key
                 * @param value {object} value
                 */
                set: function (key, value) {
                    // 如果为多个元素，直接返回null
                    if ($element.length !== 1) {
                        return null;
                    }
                    if (EUtil.isBlank(serialNumber)) {
                        serialNumber = EUtilCacheSN++;
                        $element.attr('data-' + snKey, serialNumber);
                        EUtilCache[serialNumber] = {};
                    }
                    EUtilCache[serialNumber][key] = value;
                },
                /**
                 * 查找元素变量中是否有指定`key`
                 *
                 * @param key {string} key
                 * @returns {boolean} true/false
                 */
                has: function (key) {
                    // 如果为多个元素或者data-e-cache为空，无法获取对象，直接返回false
                    if ($element.length !== 1 || EUtil.isBlank(serialNumber)) {
                        return false;
                    }
                    return EUtilCache[serialNumber] && EUtilCache[serialNumber][key];
                },
                /**
                 * 根据`key`删除元素缓存的`value`
                 *
                 * @param key {string} key
                 */
                remove: function (key) {
                    // 如果为多个元素或者data-e-cache为空，无法获取对象，直接返回null
                    if ($element.length === 1 && EUtil.isNotBlank(serialNumber)) {
                        delete EUtilCache[serialNumber][key];
                    }
                }
            }
        },
        /**
         * 根据参数名称从url中获取参数值，如没有返回`null`
         *
         * @param {string} paramName 参数名
         * @returns {string|null}
         */
        getURLParam: function (paramName) {
            let searchString = window.location.search.substring(1),
                i, val, params = searchString.split("&");

            for (i = 0; i < params.length; i++) {
                val = params[i].split("=");
                if (val[0] === paramName) {
                    return unescape(val[1]);
                }
            }

            return null;
        },

        /**
         * 根据窗口判断是否属于移动设备(宽度小于lg)
         *
         * @returns {boolean} true/false
         */
        isMobileDevice: function () {
            return this.getViewPort().width < this.getBreakpoint('lg');
        },

        /**
         * 根据窗口判断是否属于桌面设备(宽度大于lg)
         *
         * @returns {boolean} true/false
         */
        isDesktopDevice: function () {
            return !EUtil.isMobileDevice();
        },

        /**
         * 获取浏览器窗口尺寸
         *
         * @returns {object} eg: {width: 1920, height: 1080}
         */
        getViewPort: function () {
            let e = window,
                a = 'inner';
            if (!('innerWidth' in window)) {
                a = 'client';
                e = document.documentElement || document.body;
            }

            return {
                width: e[a + 'Width'],
                height: e[a + 'Height']
            };
        },

        /**
         * 检查当前设备是否为指定类型
         *
         * @param {string} mode 类型(e.g: desktop, desktop-and-tablet, tablet, tablet-and-mobile, mobile)
         * @returns {boolean} true/false
         */
        isInResponsiveRange: function (mode) {
            // 浏览器宽度
            let breakpoint = this.getViewPort().width;

            if (mode === 'desktop' && breakpoint >= (this.getBreakpoint('lg') + 1)) {
                return true;
            } else if (mode === 'tablet' && (breakpoint >= (this.getBreakpoint('md') + 1) && breakpoint < this.getBreakpoint('lg'))) {
                return true;
            } else if (mode === 'mobile' && breakpoint <= this.getBreakpoint('md')) {
                return true;
            } else if (mode === 'desktop-and-tablet' && breakpoint >= (this.getBreakpoint('md') + 1)) {
                return true;
            } else if (mode === 'tablet-and-mobile' && breakpoint <= this.getBreakpoint('lg')) {
                return true;
            } else if (mode === 'minimal-desktop-and-below' && breakpoint <= this.getBreakpoint('xl')) {
                return true;
            } else if (mode === 'big-screen' && breakpoint <= this.getBreakpoint('xxl')) {
                return true;
            } else if (mode === 'super-screen' && breakpoint <= this.getBreakpoint('xxxl')) {
                return true;
            }

            return false;
        },
        /**
         * 检查是否为IE系列浏览器，如果是则返回版本，如果不是返回`false`
         *
         * @returns {boolean|number}
         */
        detectIE: function () {
            let ua = window.navigator.userAgent;

            let msie = ua.indexOf('MSIE ');
            if (msie > 0) {
                // IE 10 or older => return 版本号
                return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
            }

            let trident = ua.indexOf('Trident/');
            if (trident > 0) {
                // IE 11 => return 版本号
                let rv = ua.indexOf('rv:');
                return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
            }

            let edge = ua.indexOf('Edge/');
            if (edge > 0) {
                // Edge (IE 12+) => return 版本号
                return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
            }

            // 其他浏览器
            return false;
        },
        /**
         * 生成一个指定前缀的 unique ID
         *
         * @param {string} prefix {string} 前缀
         * @returns {string}
         */
        getUniqueID: function (prefix) {
            return prefix + Math.floor(Math.random() * (new Date()).getTime());
        },

        /**
         * 获取在`breakpoints`中定义的响应式前缀对应的`width`
         *
         * @param mode {string} 响应式模式前缀 (e.g: xxl, xl, lg, md, sm)
         * @returns {number} width
         */
        getBreakpoint: function (mode) {
            return breakpoints[mode];
        },

        /**
         * 获取指定最小和最大范围内随机生成的整数值
         *
         * @param {number} min 最小
         * @param {number} max 最大
         * @returns {number}
         */
        getRandomInt: function (min, max) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        },
        /**
         * css3 动画结束事件
         *
         * @param element {element} 目标元素
         * @param callback {function} 回调函数
         */
        transitionEnd: function (element, callback) {
            let transition;
            let transitions = {
                transition: 'transitionend',
                OTransition: 'oTransitionEnd',
                MozTransition: 'mozTransitionEnd',
                WebkitTransition: 'webkitTransitionEnd',
                msTransition: 'msTransitionEnd'
            };

            for (let t in transitions) {
                if (element.style[t] !== undefined) {
                    transition = transitions[t];
                }
            }

            EUtil.one(element, transition, callback);
        },
        /**
         * 初始化滚动条插件`PerfectScrollbar`
         *
         * @param element {element|string} 目标元素，支持`选择器`、`Element`、`jQuery对象`
         * @param options {object} 选项
         */
        scrollInit: function (element, options) {
            if (!element) return;
            let $element = $(element);

            // 初始化
            function init() {
                let ps;
                let height;
                // 如果高度是function
                if (options.height instanceof Function) {
                    height = parseInt(options.height.call());
                } else {
                    height = parseInt(options.height);
                }
                ps = EUtil.data($element).get('ps');
                // 如果设置mobileNativeScroll: true，当当前窗口模式为pad & mobile时，销毁滚动条
                if ((options.disableForMobile) && EUtil.isInResponsiveRange('tablet-and-mobile')) {
                    if (ps) {
                        if (options.resetHeightOnDestroy) {
                            // 销毁时高度设置为auto
                            $element.css('height', 'auto');
                        } else {
                            $element.css('overflow', 'auto');
                            if (height > 0) {
                                $element.css('height', height + 'px');
                            }
                        }
                        // 销毁滚动条
                        ps.destroy();
                        EUtil.data($element).remove('ps');
                    } else if (height > 0) {
                        $element.css('overflow', 'auto');
                        $element.css('height', height + 'px');
                    }
                    return;
                }

                if (height > 0) {
                    $element.css('height', height + 'px');
                }

                $element.css('overflow', 'hidden');

                if (ps) {
                    // 如果已经初始化，直接update
                    ps.update();
                } else {
                    $element.addClass('e-scroll');
                    ps = new PerfectScrollbar(element, {
                        wheelSpeed: 0.5,
                        swipeEasing: true,
                        wheelPropagation: false, // 当滚动到底时是否继续滚动父元素
                        minScrollbarLength: 40,
                        maxScrollbarLength: 300,
                        suppressScrollX: !$element.data('scroll-x') // 开启水平方向滚动条
                    });
                    EUtil.data($element).set('ps', ps);
                }
            }

            // 初始化
            init();
            // 如果开启了窗口拖动更新滚动条
            if (options.handleWindowResize) {
                $(window).on('resize', init);
            }
        },
        /**
         * 更新滚动条插件`PerfectScrollbar`
         *
         * @param element {element|string} 目标元素，支持`选择器`、`Element`、`jQuery对象`
         */
        scrollUpdate: function (element) {
            let ps = EUtil.data(element).get('ps');
            if (ps) {
                ps.update();
            }
        },
        /**
         * 更新页面中所有滚动条插件`PerfectScrollbar`
         *
         * @param elements {element|string} 目标元素，支持`选择器`、`Element`、`jQuery对象`
         */
        scrollUpdateAll: function (elements) {
            $(elements).find('.ps').each(function () {
                EUtil.scrollUpdate(this);
            });
        },
        /**
         * 销毁滚动条插件`PerfectScrollbar`
         *
         * @param element {element|string} 目标元素，支持`选择器`、`Element`、`jQuery对象`
         */
        scrollDestroy: function (element) {
            let ps = EUtil.data(element).get('ps');
            if (ps) {
                ps.destroy();
                EUtil.data(element).remove('ps');
            }
        },
        /**
         * 将运动条滚动到指定位置
         *
         * @param target {element|string} 目标元素，支持`选择器`、`Element`、`jQuery对象`
         * @param top {number} 滚动到什么位置
         * @param speed {number} 动画时长，默认`500ms`
         */
        scrollTo: function (target, top, speed) {
            if (!target) {
                target = 'html, body';
            }
            if (!speed) {
                speed = 500;
            }
            $(target).animate({scrollTop: top}, speed);
        },
        /**
         * 判断对象是否为`undefined`或`null`，反方法isNotNull(obj)`，如果要判断字符串是否为空请用`isBlank(str)`
         *
         * @param obj {object} 待检查对象
         * @returns {boolean} true/false
         */
        isNull: function (obj) {
            return typeof obj === 'undefined' || obj === null;
        },
        /**
         * 判断对象是否不为`undefined`或`null`，如果要判断字符串是否为空请用`isNotBlank(str)`
         *
         * @param obj {object} 待检查对象
         * @returns {boolean} true/false
         */
        isNotNull: function (obj) {
            return !EUtil.isNull(obj);
        },
        /**
         * 判断字符串是否为`undefined`、`null`或`''`，反方法`isNotBlank(str)`
         *
         * @param str {string} 待检查字符串
         * @returns {boolean} true/false
         */
        isBlank: function (str) {
            return typeof str === 'undefined' || str === null || str === '';
        },
        /**
         * 判断字符串是否不为`undefined`、`null`或`''`
         *
         * @param str {string} 待检查字符串
         * @returns {boolean} true/false
         */
        isNotBlank: function (str) {
            return !EUtil.isBlank(str);
        },
        /**
         * 检查对象是否为Function类型
         *
         * @param fn {object} 待检查function
         * @returns {boolean} true/false
         */
        isFunction: function (fn) {
            return Object.prototype.toString.call(fn) === '[object Function]';
        },
        /**
         * 判断是否是数字，如果不是数字类型尝试转为数字类型，如果发生异常则返回`false`（方法不会抛出异常）
         *
         * @param obj {object} 待检查number
         * @returns {boolean} true/false
         */
        isNumber: function (obj) {
            // 是否为数字类型
            if (typeof obj === 'number') {
                return true;
            }
            // 如果不是，尝试转为数字类型，如果有异常，则返回false
            try {
                Number(obj);
                return true;
            } catch (e) {
                return false;
            }
        },
        /**
         * 检查对象是否是数组类型
         *
         * @param obj {object} 待检查对象
         * @returns {boolean} true/false
         */
        isArray: function (obj) {
            return obj && Array.isArray(obj);
        },
        /**
         * 判断是否为字符串类型
         *
         * @param obj {object} 待检查对象
         * @returns {boolean} true/false
         */
        isString: function (obj) {
            return typeof obj === 'string';
        },
        /**
         * 截取指定长度字符串，如果超出在后面拼接`...`
         *
         * @param str {string} 字符串
         * @param length {number} 长度
         * @returns {string} 截取后字符串
         */
        subStr: function (str, length) {
            if (str.length > length) {
                return str.substr(0, length) + "...";
            } else {
                return str;
            }
        },
        /**
         * 是否是顶级页面，根据最上层页面（window.top）是否是自己
         *
         * @return {boolean} true/false
         */
        isTopPage: function () {
            return window.top === window.self;
        },
        /**
         * 弹出toastr
         *
         * @param config {object} options
         */
        toastr: function (config) {
            if (EUtil.isTopPage()) {
                toastr[config.type](config.subtitle, config.title);
            } else {
                window.parent.EUtil.toastr(config);
            }
        },
        /**
         * 弹出提示（会查找顶级页面然后调用`SweetAlert2`中的`fire()`）
         *
         * @param config {object} option
         * @param callback {function} 回调
         */
        alert: function (config, callback) {
            if (EUtil.isTopPage()) {
                swal.fire(config).then(function (result) {
                    if (EUtil.isFunction(callback)) {
                        // todo: 检查`callback`是否正确调用
                        callback(result);
                    }
                });
            } else {
                window.parent.EUtil.alert(config, callback);
            }
        },
        /**
         * 弹出提示（级别: info-信息；会查找顶级页面然后调用`SweetAlert2`中的`fire()`）
         *
         * @param title {string} 标题
         * @param subtitle {string} 副标题
         */
        alertInfo: function (title, subtitle) {
            if (EUtil.isTopPage()) {
                swal.fire(title, subtitle, 'info');
            } else {
                window.parent.EUtil.alertInfo(title, subtitle);
            }
        },
        /**
         * 弹出提示（级别: success-成功；会查找顶级页面然后调用`SweetAlert2`中的`fire()`）
         *
         * @param title {string} 标题
         * @param subtitle {string} 副标题
         */
        alertSuccess: function (title, subtitle) {
            if (EUtil.isTopPage()) {
                swal.fire(title, subtitle, 'success');
            } else {
                window.parent.EUtil.alertSuccess(title, subtitle);
            }
        },
        /**
         * * 弹出提示（级别: warning-警告；会查找顶级页面然后调用`SweetAlert2`中的`fire()`）
         *
         * @param title {string} 标题
         * @param subtitle {string} 副标题
         */
        alertWarning: function (title, subtitle) {
            if (EUtil.isTopPage()) {
                swal.fire(title, subtitle, "warning");
            } else {
                window.parent.EUtil.alertWarning(title, subtitle);
            }
        },
        /**
         * 弹出提示（级别: error-错误；会查找顶级页面然后调用`SweetAlert2`中的`fire()`）
         *
         * @param title {string} 标题
         * @param subtitle {string} 副标题
         */
        alertError: function (title, subtitle) {
            if (EUtil.isTopPage()) {
                swal.fire(title, subtitle, "error");
            } else {
                window.parent.EUtil.alertError(title, subtitle);
            }
        },
        /**
         * 询问提示（会查找顶级页面然后调用`SweetAlert2`中的`fire()`）
         *
         * @param title {string} 标题
         * @param subtitle {string} 副标题
         * @param okCallback {function} 点击确定回调
         * @param cancelCallback {function} 点击取消回调
         */
        alertConfirm: function (title, subtitle, okCallback, cancelCallback) {
            if (EUtil.isTopPage()) {
                swal.fire({
                    title: title,
                    text: subtitle,
                    type: 'warning',
                    showCancelButton: true,
                    confirmButtonColor: '#DD6B55',
                    confirmButtonText: '确定',
                    cancelButtonText: '取消'
                }).then(function (obj) {
                    if (obj.value != null && obj.value) {
                        if (EUtil.isFunction(okCallback)) {
                            okCallback();
                        }
                    } else {
                        if (EUtil.isFunction(cancelCallback)) {
                            cancelCallback();
                        }
                    }
                })
            } else {
                window.parent.EUtil.alertConfirm(title, subtitle, okCallback, cancelCallback);
            }
        },
        /**
         * 显示等待遮罩
         *
         * @param element {element|string} 目标元素，支持`选择器`、`Element`、`jQuery对象`
         */
        openWait: function (element) {
            EApp.block(element, {
                message: '请稍候...'
            });
        },
        /**
         * 移除等待遮罩
         *
         * @param element {element|string} 目标元素，支持`选择器`、`Element`、`jQuery对象`
         */
        closeWait: function (element) {
            EApp.unblock(element);
        },
        /**
         * 设置按钮处理中状态，为了防止等待状态时按钮出现多个图标，如按钮有图标，会隐藏使用处理中图标代替；调用`offButtonWait()`后会恢复原有图标
         *
         * @param element {element|string} 目标元素，支持`选择器`、`Element`、`jQuery对象`
         */
        setButtonWait: function (element) {
            EApp.progress(element, null);
        },
        /**
         * 取消按钮处理中状态
         *
         * @param element {element|string} 目标元素，支持`选择器`、`Element`、`jQuery对象`
         */
        offButtonWait: function (element) {
            EApp.unProgress(element);
        },
        /**
         * 封装jquery.ajax方法，提供了一些自定义属性以提高开发效率增加用户体验；默认使用`post`请求并且`dataType: 'json'`<br/>
         * 如果`dataType: 'json'`或响应信息为`object`类型，得到响应信息后尝试解析数据是否为成功并提示；如不需要上述处理逻辑可将`option.needAlert`设置为`false`<br/>
         * 得到后端响应后，取消`option.wait`等待状态，当HttpStatus不为200时，如`option.error`已存在则调用用户传入回调，如不存在则调用`EUtil.ajaxErrorCallback(XMLHttpRequest, textStatus, errorThrown)`
         *
         * @param option {object} 参数
         * @param option.wait {string} ajax执行中需要设置等待区域的选择器
         * @param option.needAlert {boolean} 服务器响应后是否弹出提示
         */
        ajax: function (option) {
            if (EUtil.isNotNull(option)) {
                let defaultOption = {
                    cache: false,
                    type: 'post',
                    dataType: 'json'
                };
                // 合并 option
                option = $.extend(true, {}, defaultOption, option);

                if (EUtil.isNotBlank(option.wait)) {
                    EUtil.openWait(option.wait);
                }
                $.ajax({
                    async: option.async,
                    url: option.url,
                    data: option.data,
                    contentType: option.contentType,
                    type: option.type,
                    dataType: option.dataType,
                    error: function (XMLHttpRequest, textStatus, errorThrown) {
                        // 关闭等待
                        if (EUtil.isNotBlank(option.wait)) {
                            EUtil.closeWait(option.wait);
                        }
                        // 如传入error回调,则不调用公用ajaxErrorCallback
                        if (EUtil.isFunction(option.error)) {
                            // 用户自定义error，请处理EUtil.ajaxErrorCallback中提到的异常
                            option.error(XMLHttpRequest, textStatus, errorThrown);
                        } else {
                            EUtil.ajaxErrorCallback(XMLHttpRequest, textStatus, errorThrown);
                        }
                    },
                    success: function (res) {
                        // 若不成功禁止返回200
                        if (EUtil.isNotBlank(option.wait)) {
                            EUtil.closeWait(option.wait);
                        }
                        // 如果dataType未指定或者dataType为json，或者res是json对象
                        if ((EUtil.isBlank(option.dataType) || 'json' === option.dataType || typeof res === 'object') &&
                            EUtil.isNotBlank(res.code) && res.code.length === resultCode.length && option.needAlert) {
                            // 解析响应码
                            let result = ETool.analysisCode(res.code);
                            if (resultCode.code.success === result.code) {
                                // 如果成功，并且需要提示
                                EUtil.handleResult(res, null);
                            }
                        }
                        // 成功
                        if (EUtil.isFunction(option.success)) {
                            option.success(res);
                        }
                    }
                });
            } else {
                console.warn('EUtil.ajax方法不允许传入空参数');
            }
        },
        /**
         * 解析响应码并提示`message`，
         * 响应码规则请参考[响应码](/guide/standard.html#%E5%93%8D%E5%BA%94%E7%A0%81)
         *
         * @param res {object} 响应数据
         * @param callback {function|null} 回调
         */
        handleResult: function (res, callback) {
            /**
             * 弹出提示
             *
             * @param method {string} 方法 eg：successTip、infoTip、warnTip、errorTip
             * @param title {string} 标题
             * @param subtitle {string} 副标题，如果为空则不进行提示
             */
            let handleTip = function (method, title, subtitle) {
                if (EUtil.isNotBlank(subtitle)) {
                    ETool[method](title, subtitle);
                }
            };
            // 解析响应码
            let result = ETool.analysisCode(res.code);
            // 根据错误级别做不同处理
            switch (result.level) {
                case resultCode.level.success:
                    handleTip('successTip', commonTips.success, res.message);
                    break;
                case resultCode.level.warn:
                    // 警告。如果message不为空则提示，如果为空直接忽略
                    ETool.warnTip(commonTips.warn, res.message);
                    break;
                case resultCode.level.error:
                    // 错误。如果message不为空则提示，如果为空直接忽略
                    if (resultCode.modular.common === result.modular) {
                        // 公用模块
                        if (resultCode.code.badRequest === result.code) {
                            handleTip('errorTip', '参数错误', res.message);
                            break;
                        } else if (resultCode.code.unauthorized === result.code) {
                            // 会话失效 eg：登录失败、被踢出，在其他地方登录，管理员踢出
                            EUtil.alert({
                                title: '会话过期',
                                text: res.message,
                                type: 'warning',
                                confirmButtonText: '确定'
                            }, function () {
                                // 将顶级页面转到登录
                                window.top.location.href = basePath + '/login?' + res.data + '=1';
                            });
                            break;
                        } else if (resultCode.code.forbidden === result.code) {
                            // 无权访问
                            handleTip('errorTip', commonTips.forbidden, res.message);
                            // 权限可能被修改,刷新缓存用户数据
                            ETool.getUser(false);
                            break;
                        }
                    }
                    handleTip('errorTip', commonTips.fail, res.message);
                    break;
                default:
                    // 默认使用信息级别。如果message不为空则提示，如果为空直接忽略
                    handleTip('infoTip', commonTips.info, res.message);
                    break;
            }
            // 回调
            if (EUtil.isFunction(callback)) {
                callback(res);
            }
        },
        /**
         * 当后端响应`HttpStatus`为`500`时，解析错误类型并做相应处理
         *
         * @param XMLHttpRequest {object}
         * @param textStatus {object}
         * @param errorThrown {object}
         */
        ajaxErrorCallback: function (XMLHttpRequest, textStatus, errorThrown) {
            // 后端响应
            let res = XMLHttpRequest.responseJSON;
            if (typeof res === 'object' && res.code.length === resultCode.length) {
                // 如果响应码为6位，使用公用处理方法
                EUtil.handleResult(res, null);
            } else {
                if (httpStatus.badRequest === XMLHttpRequest.status) {
                    // 无效请求，一般为后端表单验证未通过
                    if (typeof res.errors !== 'undefined') {
                        let errors = [];
                        $(res.errors).each(function (index, error) {
                            errors.push(error.defaultMessage);
                        });
                        ETool.errorTip(commonTips.fail, errors.join('<br/>'));
                        return;
                    }
                }
                // 其他错误
                console.error(XMLHttpRequest.responseText);
                ETool.errorTip(XMLHttpRequest.status, res.message);
            }
        }
    }
}();

// 初始化
$(document).ready(function () {
    EUtil.init();
});